/* visStreamlineClustering.h
 * The class is designed to implement the method of streamline clustering.
 * It's done in the following way:
 * 1. randomly seed a specific number of source points in the data field;
 * 2. integrate to get the corresponding streamlines, and then rearrange 
 *    each streamline to a fashion that the beginning point is in the first 
 *    position of the vector storing the streamlines.
 * 3. compute the similarity extent for each pair of streamlines. A multiple 
 *    of well known ways to do this may be tried, such as Euclidean distance,
 *    Manhattan distance, so and on.
 * 4. cluster the streamlines according to the similarity matrix. A number
 *    of methods to do this can be empolyed, such as k-means, spectral graph 
 *    clustering method.    
 *
 * Modification history : 
 * @1. 3/3/2009,by Xinlai,Lu; ubuntu,g+4.3.2
 *    The k-means needs to access the point on the line which is also the 
 *    cluster center and is not the streamlines generated here. Therefor, parameters 
 *    of some functions need modifying. 
 *    
 *    The involved functions(five) are:
 *    getPointOnStreamline(...), EuDistance(...), minDistance(...), formalMetric(...), 
 *    computeDeSimilarity(...)
 *    And five functions reloading them are also added.
 * @2: 4/3/2009,by Xinlai,Lu; ubuntu,g++4.3.2
 *    Oops!!! An variable of type unsigned integer is devided by another variable
 *    of the same type with the hope of getting a ratio which should be of type
 *    double!!! 
 *    Silly but really!
 *    Modifications are done at the implementations of functions getPointOnStreamline(...).
 * @3: 8/3/2009,by Xinlai,Lu; ubuntu,g++4.3.2
 * 		Since the public variables and interfaces of visKMeans and visKMedoids are 
 * 		moved here, so some modifications	done before will be updated.
 * @4: 12/3/2009,by Xinlai,Lu; ubuntu,g++4.3.2
 * 		In order to display one clustering result of a certain iteration, the m_classHaveObjVec
 * 		and m_ClusterCenters are slightly modified to completely store the process of 
 * 		clustering : at a certain iteration, the centers and the cluster members are 
 * 		stored. Five functions accessing them are added. Some functions are also modified
 * 		to add parameters needed : 
 * 		initCluster() adds iteration;
 *    reshapeCluster2One adds itrt.
 * 
 * @5: 20/3/2009,by Xinlai,Lu; ubuntu,g++4.3.2
 * 		To implement the topology clustering method, a variable sources and two interfaces
 * 		(setSources and computeStreamlinesInDomain) are added.
 */

#ifndef VIS_STREAMLINE_CLUSTERING
#define VIS_STREAMLINE_CLUSTERING

#include "squareMatrixAccess.h"
#include "visStreamline.h"
//#include "f2c.h"
//extern "C"
//{
//  #include "clapack.h"
//}

namespace visualization
{   
typedef enum criterions{SQU_ERR}criterions;
typedef enum _STATUS{_CLUSTERING_END, _CLUSTERING_CONTINUE}_STATUS;
typedef enum _SCHEME{SIMPLEST, DIS_WEIGHTED}_SCHEME;

class visStreamlineClustering
{  
protected:
typedef vector<Point> strmLine;
typedef double (visStreamlineClustering:: *pf1)(const strmLine &, const strmLine&);
typedef double (visStreamlineClustering:: *pf2)(unsigned int, const strmLine&);
 
public:
  visStreamlineClustering(Model *mdl) : m_ClusterCenters(NULL), m_ObjIsClassVec(NULL),
		 											  						m_ClassHaveObjVec(NULL), m_Error(0), 
																				m_ErrCriterion(SQU_ERR), m_streamlineLength(NULL),
																				m_dataField(mdl), m_streamlines(NULL)
  {}
  visStreamlineClustering() : m_ClusterCenters(NULL), m_ObjIsClassVec(NULL),
		 											  	m_ClassHaveObjVec(NULL), m_Error(0), 
															m_ErrCriterion(SQU_ERR), m_streamlineLength(NULL),
															m_streamlines(NULL)
	{}
	void dataField(Model *mdl) { m_dataField = mdl; }
	~visStreamlineClustering()
	{
	    // After modification, now the memory for m_streamlines which containing streamlines 
			// should be released by applications itself.
	    //delete []m_streamlines;

      // @Modification 3
      // I'm very sorry to confirm you of that memory allocated to the variable has to 
			// released by the application program itself!!
      //delete []m_ClusterCenters;

  		//delete []m_ObjIsClassVec;
  		//delete []m_ClassHaveObjVec;
//			delete []m_relationMatrix;
			delete []m_streamlineLength;
			/***Give some hints***/
			cout << "REMEMBER TO RELEASE MEMORY ALLOCATED TO :" << endl <<
							 "-- streamlines "    << endl <<
							 "-- clustercenters " << endl <<
							 "-- objIsClassVec "  << endl <<
							 "-- classHaveObj "   << endl;
	}
	void releaseStreamlines()    { delete m_streamlines;	}
	void releaseClustering()     
	{
		releaseClusterCenters();
		releaseObjIsClass();
		releaseClsHaveObj();
	}
	void releaseClusterCenters() { delete []m_ClusterCenters; }
	void releaseObjIsClass()     { delete []m_ObjIsClassVec; }
	void releaseClsHaveObj()     { delete []m_ClassHaveObjVec; }

	/** This is used for topology clustering only.
	 * @Modification 5
	 */
	void setSources(vector<Point> &sourcesInDomain){m_Sources = sourcesInDomain;}
	void computeStreamlinesInDomain();
	
/**********************************1. Compute streamlines*****************************/
	void setParamFile(string file){ m_pfile = file; }
	// Set parameter
	// To specify the number of source points.
	void setSeedSources(unsigned int n)	{m_sourceNumber = n;}

	// Set parameter
	// To set the parameters used for streamline integration
	void setParameters(double step, double maxTime)
	{
	    m_stepLength = step;
	    m_maxTime = maxTime;
//			m_seg = (unsigned int)(1.5 * m_maxTime);
	}

	// Calculate
	// Integrate to compute the streamlines. Some parameters may be needed 
	// to be specified for this function.
	void computeStreamlines(void);

	// Set the segments.
	void setSegment(unsigned int s){ m_seg = s; }

	// Accesses :
	/******** THE FOLLOWING THREE FUNCTIONS ARE USED TO ACCESS THE STREAMLINES GENERATED.**********/
	// The ways to access the streamlines generated by computeStreamlines.
	// Here the returned size is half of the one in m_streamlines since later
	// streamlines in m_streamlines is going to be combined by "wrapping" them.
	size_t getStreamlinesNumber() const	{ return m_streamlines -> size()/2; }

	/**
	 * The function returns the size of a streamline. Note that the size is the number after
	 * combining the streamlines in both afterward and forward directions. 
	 * Here, the result in the "else" is substracted by 1 since a same seed is in
	 * both of the streamlines of forward and backward directions.
	 *
	 * @param i : the index of the streamline being accessed.
	 */
	size_t getStreamlineSize(size_t i) const
	{
	    if(0 > i || getStreamlinesNumber() < i) {return 0;}
	    else { return (*m_streamlines)[i].size() + (*m_streamlines)[i + getStreamlinesNumber()].size() - 1; }
	}

	/**
	 * This function gets a point on a streamline. Note that the index is not checked for validation
	 * because when using this in a for loop the function getStreamlineSize will also be invoked.
	 * @param line : the index of a line.
	 * @param pnt  : the index of a point.
	 * @return the point wanted.
	 */
	Point getPointOnStreamline(unsigned int line, unsigned int pnt) const;

	/**
	 * This function devides a streamline "equally" by several segments, each segment indicated by 
	 * a line with two vertexes, whose coordinates can be returned.
	 * !!!!!
	 * Note that, here is an implicit assumption : the step length used to calculate the streamline
	 * is sufficient small such that every segment of the streamlines can be considered as 
	 * approximately equal.
	 * !!!!!
	 * @param line : is as above.
	 * @param segments : is the number of segments used to devide the streamline indexed by line.
	 * @param pnt : the pnt-th point on the segmeneted streamline devided by segments.
	 * @param p : to store the returned coordinates.
	 */
	 void getPointOnStreamline(unsigned int line, const unsigned int segments, unsigned int pnt, Point &p) const;
	/** 
	 * @Modification 1: The function is added for a streamline from of type vector<Point>, taking a cluster
	 * center for example.
	 * @param line : the streamline of type vector<Point>
	 */
	 void getPointOnStreamline(const strmLine &line, const vector<double> &lineLen, const unsigned int segments,
	 													 unsigned int pnt, Point &p) const;
	 
/*******************************2. Compute the similarity matrix*************************/
public:
	/**
	 * Compute the Euclidean distance. The lenght of the two streamlines may be different, while the
	 * function needs points on the lines to calculate the distance. To overcome this, the functions 
	 * simply takes the shortest of the two, which may be improved later.
	 * Modification @1. The original one is:
	 * double EuDistance(unsigned int line, unsigned int line)
	*/
	double EuDistance(const strmLine &, const strmLine &);
	/**
	 * The function is for a streamline from m_streamlines and a streamline of type strmLine.
	 * @param line : u
	 */
	double EuDistance(unsigned int line, const strmLine &);
	double EuDistance(unsigned int line1, unsigned int line2);
	
	/**
	 * Walvlet transform distance
	 * Modification @1. The original one is:
	 * double minDistance(unsigned int line, unsigned int line)
	 */
	double minDistance(strmLine&, strmLine &);
	double minDistance(size_t line1, size_t line2);
	/**
	 * The function is for a streamline from m_streamlines and a streamline of type strmLine.
	 */
	double minDistance(unsigned int line, strmLine &);

	/* Similarity distance used by formal paper.
	 * Modification @1. The original one is:
	 * double formalMetric(unsigned int line, unsigned int line)
	 */
	double formalMetric(strmLine &, strmLine &);
	/**
	 * The function is for a streamline from m_streamlines and a streamline of type strmLine.
	 */
	double formalMetric(unsigned int line, strmLine &);
//
//	/**
//	 * The function computes the desimilarity of each pair of streamline and save the results
//	 * in m_relationMatrix.
//	 * Since the one calculated is desimilarity, so the sel-desimilarity is set to zero.
//	 *
//	 * @param pSimFun : the function invoked to calculate the desimilarity.
//	 */
//	void computeDeSimilarity(pf1 pDeSimFun = &visStreamlineClustering::EuDistance);
//
//
	/**
	 * The function calculate and return the desimilarity value.
	 * Modification @1. The original one is:
	 * double computeDeSimilarity(unsigned int line1,unsigned int line2,
	 * 														pf1 pDeSimFun=&visStreamlineClustering::EuDistance);
	 */
	double computeDeSimilarity(strmLine &line1, strmLine &line2,
	                           pf1 pDeSimFun = &visStreamlineClustering::EuDistance);
	/**
	 * @param line1 : the index of a line in m_streamlines.
	 */
	double computeDeSimilarity(unsigned int line1, strmLine &line2, 
														 pf2 pDeSimFun = &visStreamlineClustering::EuDistance);

	/**
	 * In some cases, similarity matrix is needed, so the result from computeDeSimilarity is transformed 
	 * as needed.
	 *
	 * !!!!!!!!!!!!!!!How about self-similarity???!!!!!!!!!!
	 *
	 */
	void transformDeSim2SimMatrix();

/******************************************3. Clustering ****************************************/
	/**
	 *
	 */
	void setErrCriterion(criterions err_criterion = SQU_ERR) { m_ErrCriterion = err_criterion; }

	/**
	 * The function to calculate the error criterion. Several methods may be employed for this
	 * purpose.
	 * @param err_criterion : the variable of type of enum to indicate the type of error crite-
	 * rion to used here.
	 * @return : the error for the current clustering results.
	 */
	double totalError();

	/**
	 * The function initialize the K centers : the number of clusters wanted, the positions
	 * of k cluster centers, and also invoke the function totalError to calculate the initial 
	 * error criterion.
	 *
	 * !!!Note that here the k initial cluster is simply choozed as the first k from the st-
	 * reamlines in that the sources of the streamlines are randomly selected.
	 *
	 * @param k : the parameter for the number of clusters.
	 *
	 * @Modification 4 : the parameter iteration is added.
	 * @param iteration : the number of iterations when clustering.
	 */
	void initClusters(unsigned int k, unsigned int iteration);
	
	/**
	 * The function is used to combine several streamlines in one cluster to a single streamline,
	 * and then store the result. There should be several schemes.
	 *
	 * @param schm : the scheme used to combine a cluster into one.
	 * !!!!!!!!!!!!!!!!!
	 * @Modification 4 : 
	 * @param itrt : the current iteration number. If there are k iterations then the range of the itrt
	 * is (0, 1, 2, ..., itrtNum-1), ie. the parameter starts from 0.
	 */
	void reshapeCluster2One(_SCHEME schm, unsigned int itrt);

	/**
	 * The function returns the clustering results.
	 * @return : the object of type cluster storing the cluster results.
	 */
	vector<Point> *getClusterCenters() { return m_ClusterCenters; }
	vector<Point> *getClusterCenter(unsigned int index)	{	return &m_ClusterCenters[index]; }

	/**
	 *
	 */
	vector<unsigned int> *getClsHaveObj() { return m_ClassHaveObjVec; }
	vector<unsigned int> &getClsHaveObj(unsigned int i) { return m_ClassHaveObjVec[i]; }

	/**
	 * Used to compute the streamline information which will be stored in m_streamlineLength.
	 */
	void computeLength();
	/**
	 * Used to compute length infomation of streamline with the result stored int m_tmpLen;
	 */
	void computeLength(const strmLine &line, vector<double> &tmpLen);

	/**
	 * Used to searching the variable streamline length infomation.
	 */
	unsigned int binarySearch(const vector<double> &data, double key)const;

protected :
	/**
	 * @Modification 4 :
	 * An array of size m_NumClusters*(m_itrt+1) to store the cluster centers of each iteration.
	 * Note that the first m_itrt items are for the initial choozen cluster centers, while the
	 * remaining m_NumClusters*m_itrt is for the m_itrt interations.
	 */
	vector<Point> *m_ClusterCenters;
	/* An array of the same size of the number of objects with the available value ranging from 
	 * 0 to m_NumClusters - 1, indicating which class an object blongs to.
	 */	
	unsigned int *m_ObjIsClassVec;
	/**
	 * An array stores the information that which class contains which objects.
	 * @Modification 4 : 
	 * the size that will be allocated to it is m_NumClusters*m_itrt to store the cluster member
	 * of each cluster during clustering.
	 */
	vector<unsigned int> *m_ClassHaveObjVec;
	unsigned int m_NumClusters;
	double m_Error;
	criterions m_ErrCriterion;

	// @Modification 4: the variable is added.
	unsigned int m_itrt;

	/**
	 * @Modification 4 : five functions are added to access m_ClusterCenters and m_ClassHaveObjVec.
	 * @param itrt : iteration number, from 0 on;
	 * @param cls : the cluster index number.
	 */
	/**
	 * Get all of the points of the center.
	 */
	vector<Point> &getClusterCenters(unsigned int itrt, unsigned int cls)
	{ return m_ClusterCenters[itrt*m_NumClusters + cls]; }
	/**
	 * Get one point of the center.
	 */
	Point &getClusterCenter(unsigned int itrt, unsigned int cls, unsigned int pnt)
	{ return m_ClusterCenters[itrt*m_NumClusters + cls][pnt]; }
	void setClusterCenter(unsigned int itrt, unsigned int cls, Point &pnt)
	{ m_ClusterCenters[itrt*m_NumClusters + cls].push_back(pnt); }
	
	/**
	 * Get all of the members of a cluster.
	 */
	vector<unsigned int> &getClusterMembers(unsigned int itrt, unsigned int cls)
	{ return m_ClassHaveObjVec[itrt*m_NumClusters + cls]; }
	/**
	 * Get a certain member of a cluster.
	 */
	unsigned int getClusterMember(unsigned int itrt, unsigned int cls, unsigned int mem)
	{ return m_ClassHaveObjVec[itrt*m_NumClusters + cls][mem]; }
	void setClusterMember(unsigned int itrt, unsigned int cls, unsigned int mem)
	{ m_ClassHaveObjVec[itrt*m_NumClusters + cls].push_back(mem); }

public:
	/**
	 * The variable to store the length infomation of streamlines. The variable will a of size
	 * streamlines' number, with each item a vector storing the distance information of a 
	 * streamline in this format : the distance from a point(containing the starting point) on
	 * streamline to the starting point of the streamline.
	 */
	vector<double> *m_streamlineLength;
	
	vector<Point> m_Sources; // This is used for topology clustering only.
  Model *m_dataField;
private:
	// The data field

/*********************************1. Compute streamlines*********************************/
	string m_pfile;	
	// The number of sources int the data field.	
	unsigned int m_sourceNumber;
	// Integration step length
	double m_stepLength;
	// Maximum propagation time
	double m_maxTime;
	// To save the resulted streamlines
	intgResults *m_streamlines; 

	/** Parameters related to clustering **/
	double m_Interval;
protected:
	size_t m_seg; // The setSegment function and default setting are now empty.// The setSegment is
	double m_xmin, m_xmax, m_ymin, m_ymax, m_zmin, m_zmax;
	// uncommented again...
private:
	void determineIntervalAndSeg();
	//double m_xmin, m_xmax, m_ymin, m_ymax, m_zmin, m_zmax;
	size_t m_Dim;
public:
	double defaultInterval(){ return m_Interval; }
	size_t useDefaultSegment(){ return m_seg;	}

};// Class visStreamlineClustering
} // Namespace visualization

#endif
